package jfile

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"os"
	"regexp"
	"strings"
	"unicode/utf8"

	"golang.org/x/net/html/charset"
	"golang.org/x/text/encoding"
	"golang.org/x/text/encoding/charmap"
	"golang.org/x/text/encoding/simplifiedchinese"
	"golang.org/x/text/encoding/traditionalchinese"
	"golang.org/x/text/encoding/unicode"
	"golang.org/x/text/transform"
)

// ReadFile 读取文件并返回按行切分的字符串切片指针。
// 支持将常见非 UTF-8 编码（包括 GBK/GB18030/Big5/ISO-8859-1/Windows-1252/UTF-16）转换为 UTF-8。
func ReadFile(filePath string) (*[]string, error) {
	// 检查文件是否存在
	if _, err := os.Stat(filePath); os.IsNotExist(err) {
		return nil, fmt.Errorf("文件不存在: %s", filePath)
	}

	// 打开文件读取
	file, err := os.Open(filePath)
	if err != nil {
		return nil, fmt.Errorf("打开文件失败: %w", err)
	}
	defer file.Close()

	rawBytes, err := io.ReadAll(file)
	if err != nil {
		return nil, fmt.Errorf("读取文件失败: %w", err)
	}

	if len(rawBytes) == 0 {
		empty := []string{}
		return &empty, nil
	}

	utf8Content, encodingUsed, err := detectChineseAndConvert(rawBytes)
	if err != nil {
		return nil, fmt.Errorf("中文编码转换失败: %w", err)
	}

	if strings.ToLower(encodingUsed) != "utf-8" {
		fmt.Printf("检测到编码 %s 已转换为 UTF-8\n", encodingUsed)
	}

	lines := splitChineseLines(utf8Content)
	return &lines, nil
}

// ConverAsUtf8ToFile 将文件内容转码为UTF-8并保存到原文件或新路径
func ConverAsUtf8ToFile(sourcePath string, targetPath string) error {
	// 读取原始文件内容
	file, err := os.Open(sourcePath)
	if err != nil {
		return fmt.Errorf("打开文件失败: %w", err)
	}
	defer file.Close()

	rawBytes, err := io.ReadAll(file)
	if err != nil {
		return fmt.Errorf("读取文件失败: %w", err)
	}

	if len(rawBytes) == 0 {
		// 如果文件为空，根据需要决定是否需要处理
		return nil
	}

	// 检测并转换编码
	utf8Content, encodingUsed, err := detectChineseAndConvert(rawBytes)
	if err != nil {
		return fmt.Errorf("中文编码转换失败: %w", err)
	}

	// 确定输出路径
	outputPathStr := targetPath

	// 保存转换后的内容
	err = os.WriteFile(outputPathStr, []byte(utf8Content), 0644)
	if err != nil {
		return fmt.Errorf("保存文件失败: %w", err)
	}

	if strings.ToLower(encodingUsed) != "utf-8" {
		fmt.Printf("文件 %s 已从 %s 编码转换为 UTF-8 并保存到 %s\n", sourcePath, encodingUsed, outputPathStr)
	} else {
		fmt.Printf("文件 %s 已经是 UTF-8 编码，已保存到 %s\n", sourcePath, outputPathStr)
	}

	return nil
}

// detectChineseAndConvert 针对中文编码的检测与转换
func detectChineseAndConvert(rawBytes []byte) (string, string, error) {
	// 先检查 BOM
	if hasBOM, bomType := checkBOM(rawBytes); hasBOM {
		contentWithoutBOM := removeBOM(rawBytes)
		switch bomType {
		case "utf-8":
			return string(contentWithoutBOM), "utf-8", nil
		case "utf-16le":
			utf8Content, err := convertUTF16(contentWithoutBOM, unicode.LittleEndian)
			return utf8Content, "utf-16le", err
		case "utf-16be":
			utf8Content, err := convertUTF16(contentWithoutBOM, unicode.BigEndian)
			return utf8Content, "utf-16be", err
		}
	}

	// 优先判断是否为有效的 UTF-8（避免将有效的 UTF-8 错误识别为 GBK）
	if utf8.Valid(rawBytes) {
		return string(rawBytes), "utf-8", nil
	}

	// 方法1: 统计特征识别
	if encType := detectByStatisticalAnalysis(rawBytes); encType != "" {
		if s, err := convertByEncodingType(rawBytes, encType); err == nil && containsValidChinese(s) {
			return s, encType, nil
		}
	}

	// 方法2: 中文模式正则识别
	if encType := detectByChinesePattern(rawBytes); encType != "" {
		if s, err := convertByEncodingType(rawBytes, encType); err == nil && containsValidChinese(s) {
			return s, encType, nil
		}
	}

	// 方法3: 使用 html/charset 自动检测
	if enc, name, _ := charset.DetermineEncoding(rawBytes, ""); enc != nil {
		reader := transform.NewReader(bytes.NewReader(rawBytes), enc.NewDecoder())
		decoded, err := io.ReadAll(reader)
		if err == nil {
			if containsValidChinese(string(decoded)) {
				return string(decoded), name, nil
			}
		}
	}

	// 方法4: 逐一尝试常见编码
	encodingsToTry := []struct {
		name string
		enc  encoding.Encoding
	}{
		{"gbk", simplifiedchinese.GBK},
		{"gb18030", simplifiedchinese.GB18030},
		{"big5", traditionalchinese.Big5},
		{"windows-1252", charmap.Windows1252},
		{"iso-8859-1", charmap.ISO8859_1},
	}

	for _, item := range encodingsToTry {
		if s, err := convertUsingEncoder(rawBytes, item.enc); err == nil && containsValidChinese(s) {
			return s, item.name, nil
		}
	}

	// 最后尝试当作 UTF-8
	if utf8.Valid(rawBytes) && containsValidChinese(string(rawBytes)) {
		return string(rawBytes), "utf-8", nil
	}

	return "", "", fmt.Errorf("无法确定中文文件的编码格式")
}

// checkBOM 检查 BOM
func checkBOM(data []byte) (bool, string) {
	if len(data) >= 3 && data[0] == 0xEF && data[1] == 0xBB && data[2] == 0xBF {
		return true, "utf-8"
	}
	if len(data) >= 2 && data[0] == 0xFF && data[1] == 0xFE {
		return true, "utf-16le"
	}
	if len(data) >= 2 && data[0] == 0xFE && data[1] == 0xFF {
		return true, "utf-16be"
	}
	return false, ""
}

// removeBOM 移除 BOM（如果存在）
func removeBOM(data []byte) []byte {
	if len(data) >= 3 && data[0] == 0xEF && data[1] == 0xBB && data[2] == 0xBF {
		return data[3:]
	}
	if len(data) >= 2 && ((data[0] == 0xFF && data[1] == 0xFE) || (data[0] == 0xFE && data[1] == 0xFF)) {
		return data[2:]
	}
	return data
}

// detectByStatisticalAnalysis 通过简单统计启发式判断编码（GBK/Big5）
func detectByStatisticalAnalysis(data []byte) string {
	if isLikelyGBK(data) {
		return "gbk"
	}
	if isLikelyBig5(data) {
		return "big5"
	}
	return ""
}

// detectByChinesePattern 通过转换后是否包含中文字符判断编码
func detectByChinesePattern(data []byte) string {
	chineseCharRegex := regexp.MustCompile(`[\p{Han}]`)
	testEncodings := []string{"gbk", "gb18030", "big5", "iso-8859-1", "windows-1252"}

	for _, enc := range testEncodings {
		if content, err := convertByEncodingType(data, enc); err == nil {
			if chineseCharRegex.MatchString(content) {
				return enc
			}
		}
	}
	return ""
}

// convertByEncodingType 根据名称转换
func convertByEncodingType(data []byte, encodingType string) (string, error) {
	var dec transform.Transformer

	switch strings.ToLower(encodingType) {
	case "gbk", "gb2312":
		dec = simplifiedchinese.GBK.NewDecoder()
	case "gb18030":
		dec = simplifiedchinese.GB18030.NewDecoder()
	case "big5":
		dec = traditionalchinese.Big5.NewDecoder()
	case "iso-8859-1", "latin1":
		dec = charmap.ISO8859_1.NewDecoder()
	case "windows-1252":
		dec = charmap.Windows1252.NewDecoder()
	default:
		return "", fmt.Errorf("不支持的编码类型: %s", encodingType)
	}

	reader := transform.NewReader(bytes.NewReader(data), dec)
	decoded, err := io.ReadAll(reader)
	if err != nil {
		return "", err
	}
	return string(decoded), nil
}

// convertUsingEncoder 使用 encoding.Encoding 进行转换
func convertUsingEncoder(data []byte, enc encoding.Encoding) (string, error) {
	reader := transform.NewReader(bytes.NewReader(data), enc.NewDecoder())
	decoded, err := io.ReadAll(reader)
	if err != nil {
		return "", err
	}
	return string(decoded), nil
}

// containsValidChinese 检查是否包含汉字
func containsValidChinese(content string) bool {
	chineseRegex := regexp.MustCompile(`[\p{Han}]`)
	return chineseRegex.MatchString(content)
}

// isLikelyGBK 简单启发式判断 GBK（基于字节范围）
func isLikelyGBK(data []byte) bool {
	if len(data) < 2 {
		return false
	}
	count := 0
	total := 0
	limit := len(data)
	if limit > 2000 {
		limit = 2000
	}
	for i := 0; i+1 < limit; i++ {
		b1 := data[i]
		b2 := data[i+1]
		// GBK 两字节汉字：第一个字节 0x81-0xFE，第二个字节 0x40-0xFE（某些值受限，但这里宽松判断）
		if b1 >= 0x81 && b1 <= 0xFE && (b2 >= 0x40 && b2 <= 0xFE) {
			count++
			i++ // 跳过已成对的第二字节
		}
		total++
		if total > 1000 {
			break
		}
	}
	return total > 10 && float64(count)/float64(total) > 0.08
}

// isLikelyBig5 简单启发式判断 Big5
func isLikelyBig5(data []byte) bool {
	if len(data) < 2 {
		return false
	}
	count := 0
	total := 0
	limit := len(data)
	if limit > 2000 {
		limit = 2000
	}
	for i := 0; i+1 < limit; i++ {
		b1 := data[i]
		b2 := data[i+1]
		// Big5 两字节：首字节 0xA1-0xF9，次字节 0x40-0x7E 或 0xA1-0xFE
		if b1 >= 0xA1 && b1 <= 0xF9 && ((b2 >= 0x40 && b2 <= 0x7E) || (b2 >= 0xA1 && b2 <= 0xFE)) {
			count++
			i++
		}
		total++
		if total > 1000 {
			break
		}
	}
	return total > 10 && float64(count)/float64(total) > 0.08
}

// splitChineseLines 针对中文文本的行切分（去除空行与首尾空白）
func splitChineseLines(content string) []string {
	var lines []string
	scanner := bufio.NewScanner(strings.NewReader(content))
	for scanner.Scan() {
		line := strings.TrimSpace(scanner.Text())
		if line != "" {
			lines = append(lines, line)
		}
	}
	if err := scanner.Err(); err != nil {
		// 退回到简单拆分策略
		tempLines := strings.Split(content, "\n")
		for _, l := range tempLines {
			clean := strings.TrimSpace(strings.TrimRight(l, "\r"))
			if clean != "" {
				lines = append(lines, clean)
			}
		}
	}
	return lines
}

// convertUTF16 使用 unicode 包对 UTF-16 LE/BE 解码为 UTF-8
func convertUTF16(data []byte, order unicode.Endianness) (string, error) {
	var enc encoding.Encoding
	if order == unicode.LittleEndian {
		enc = unicode.UTF16(unicode.LittleEndian, unicode.IgnoreBOM)
	} else {
		enc = unicode.UTF16(unicode.BigEndian, unicode.IgnoreBOM)
	}
	reader := transform.NewReader(bytes.NewReader(data), enc.NewDecoder())
	decoded, err := io.ReadAll(reader)
	if err != nil {
		return "", err
	}
	return string(decoded), nil
}

// GetChineseFileInfo 获取文件信息用于调试
func GetChineseFileInfo(filePath string) (map[string]interface{}, error) {
	info := make(map[string]interface{})

	file, err := os.Open(filePath)
	if err != nil {
		return nil, err
	}
	defer file.Close()

	data, err := io.ReadAll(file)
	if err != nil {
		return nil, err
	}

	info["file_size"] = len(data)

	if hasBOM, bomType := checkBOM(data); hasBOM {
		info["has_bom"] = true
		info["bom_type"] = bomType
	} else {
		info["has_bom"] = false
	}

	sampleSize := min(20, len(data))
	info["first_bytes"] = fmt.Sprintf("%X", data[:sampleSize])

	if isLikelyGBK(data) {
		info["likely_encoding"] = "GBK"
	} else if isLikelyBig5(data) {
		info["likely_encoding"] = "Big5"
	} else if utf8.Valid(data) {
		info["likely_encoding"] = "UTF-8"
	} else {
		info["likely_encoding"] = "Unknown"
	}

	return info, nil
}

func min(a, b int) int {
	if a < b {
		return a
	}
	return b
}
